Terraform v0.8.2でAmazon SESを設定してみた – IPアドレスフィルタおよびアクションの作成
はじめに
こんにちは、中山です。
少し前の話になりますが、Terraform v0.8.2がリリースされました。CHANGELOGはこちらです。さまざまなアップデートがあるのですが、個人的にはAmazon Simple Email Service(以下SES)への対応が嬉しいポイントの1つでした。というのもTerraformと同等の機能を提供しているCloudFormationは執筆時点(2016/12/28)でまだSESに対応してません。そのため、同じ環境を再現したい場合はマネジメントコンソールで頑張るか、AWS CLIでオレオレスクリプトを作成する必要がありました。両方共それなりにツライので、宣言的にSESの設定を記述できるのはとても重要なポイントだと思います。
2017年1月1日追記
よく確認したらこちらのエントリで紹介しているリソースはv0.7.0で導入されていました。。。申し訳ありません。v0.8.2で導入されたSES関連のリソースはこちらのエントリで紹介しています。
早速使ってみたので本エントリでご紹介したいと思います。今回は新規に導入されたリソースの内、メール受信に関わるものについてご紹介します。検証に利用したTerraformのバージョンは現在の最新版である0.8.2です。バージョンによって内容が異なる場合があります。その点ご了承ください。
対応しているリソース
メール受信関連のリソースは以下の通りです。
- aws_ses_receipt_rule_set
- aws_ses_receipt_rule
- aws_ses_active_receipt_rule_set
- aws_ses_receipt_filter
ご覧いただくと分かるかと思いますが、IPアドレスフィルタとアクション(とそれを定義するルールセット及びルール)に対応しています。今のところ検証用トークンの生成には非対応のようです(Externalデータソースを使えばできなくないですが)。aws_route53_recordリソースを使えばレコードセットの作成自体は可能ですが、そもそも登録するレコード自体はTerraformから作ることができないということになります。そのため、アイデンティティの作成は別途自分で実施する必要があります。
やってみる
今回はSESの各機能ごとに利用方法をご紹介します。本エントリ用に作成したコードはGitHubに置いておきました。ご自由にお使いください。
上述の通り、現在のところTerraformではアイデンティティの作成ができません。本エントリではすでに検証済みのアイデンティティが存在し、かつメール送受信のための各種レコードセットが設定済みの前提で進めます。設定方法については以下のエントリを参照してください。
IPアドレスフィルタ
はじめにメールを受信した際にIPアドレスベースの制限をするためのIPアドレスフィルタを作成してみます。関連するリソースは aws_ses_receipt_filter
です。設定可能な引数は以下の通りです。
設定 | 内容 | 必須の有無 |
---|---|---|
name |
IPアドレスフィルタ名 | Yes |
cidr |
拒否または許可するCIDR | Yes |
policy |
CIDRを拒否または許可するかどうか | Yes |
今回の設定内容としては、はじめに全てのIPアドレスを 0.0.0.0/0
で拒否し、 ses_ip_range
変数で指定したSESのメール送信時に利用されているIPアドレスレンジを許可させています。SESのIPアドレスレンジについてはこちらのブログで詳しく解説されていますが、以下のコマンドで確認可能です。
$ dig +short amazonses.com TXT | grep -F 'v=spf1' | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}' 199.255.192.0/22 199.127.232.0/22 54.240.0.0/18
SESに関連するTerraformのコードは以下のようにしてみました。
ip-filter/ses.tf
# 全て拒否 resource "aws_ses_receipt_filter" "block" { name = "block-all" cidr = "0.0.0.0/0" policy = "Block" } # SES送信サーバで利用しているIPアドレスレンジを許可 resource "aws_ses_receipt_filter" "allow" { count = "${length(var.ses_ip_range)}" name = "allow-ses-ip-range-${count.index + 1}" cidr = "${var.ses_ip_range[count.index]}" policy = "Allow" }
Terraform実行後、IPアドレスフィルタの内容を確認してみます。
$ aws ses list-receipt-filters { "Filters": [ { "IpFilter": { "Policy": "Block", "Cidr": "0.0.0.0/0" }, "Name": "block-all" }, { "IpFilter": { "Policy": "Allow", "Cidr": "199.255.192.0/22" }, "Name": "allow-ses-ip-range-1" }, { "IpFilter": { "Policy": "Allow", "Cidr": "199.127.232.0/22" }, "Name": "allow-ses-ip-range-2" }, { "IpFilter": { "Policy": "Allow", "Cidr": "54.240.0.0/18" }, "Name": "allow-ses-ip-range-3" } ] }
正常に設定されているようです。動作確認としてSESからアイデンティティに登録されているメールアドレス宛にメールを送信してみます。
# 検証済みドメインを変数に代入 $ domain=<_YOUR_VERIFICATION_DOMAIN_> # テストメールの送信 $ aws ses send-email \ --from aaa@$domain \ --to bbb@$domain \ --subject subject \ --text body { "MessageId": "01000159433e374e-8579a7ea-4e73-428d-a34b-07688cc71b3f-000000" }
メール送信後、正常にメールが受信できていれば成功です。続いてSES以外からメールを送信してみます。今回はGmailにしてみました。メール送信後、以下のようなエラーメッセージ付きのバウンスメールが届いたらIPアドレスが拒否されたと確認できます。
Delivery to the following recipient failed permanently: bbb@<_YOUR_VERIFICATION_DOMAIN_> Technical details of permanent failure: Google tried to deliver your message, but it was rejected by the server for the recipient domain <_YOUR_VERIFICATION_DOMAIN_> by inbound-smtp.us-east-1.amazonaws.com. [176.32.102.11]. The error that the other server returned was: 550 5.7.1 IP address blacklisted by recipient
アクション
続いてメールを受信した際に特定のルールにマッチしたらアクションを実行させるためのルールセットを作成してみます。関連リソースとその設定内容は以下の通りです。
aws_ses_receipt_rule_set
設定 | 内容 | 必須の有無 |
---|---|---|
name |
ルールセット名 | Yes |
aws_ses_receipt_rule
設定 | 内容 | 必須の有無 |
---|---|---|
name |
ルール名 | Yes |
rule_set_name |
関連付けるルールセット名 | Yes |
after |
ルールの適用順をどのルールの後にするか | No |
enabled |
ルールを有効化するか | No |
recipients |
検証済みメールアドレスまたはドメイン | No |
scan_enabled |
受信したメールをウイルス/スパム用にスキャンするか | No |
tls_policy |
メール受信で利用される通信でTLSを必須にするか | No |
add_header_action |
ヘッダ追加アクションの設定(下記参照) | No |
bounce_action |
バウンスアクションの設定(下記参照) | No |
lambda_action |
Lambdaアクションの設定(下記参照) | No |
s3_action |
S3アクションの設定(下記参照) | No |
sns_action |
SNSアクションの設定(下記参照) | No |
stop_action |
ストップアクションの設定(下記参照) | No |
workmail_action |
WorkMailアクションの設定(下記参照) | No |
aws_ses_active_receipt_rule_set
設定 | 内容 | 必須の有無 |
---|---|---|
name |
アクティブにするルールセット名 | Yes |
それぞれのアクションで設定可能な内容は以下の通りです。
add_header_action
設定 | 内容 | 必須の有無 |
---|---|---|
header_name |
追加するヘッダのキー | Yes |
header_value |
追加するヘッダの値 | Yes |
postion |
ルール内でのアクションの順番 | Yes |
bounce_action
設定 | 内容 | 必須の有無 |
---|---|---|
message |
バウンスメールに含めるメッセージ | Yes |
sender |
バウンスメールの送信元メールアドレス | Yes |
smtp_reply_code |
アクションにマッチさせるSMTP Replyコード | Yes |
status_code |
アクションにマッチさせるSMTPステータスコード | No |
topic_arn |
アクション実行時に通知するSNSトピックARN | No |
postion |
ルール内でのアクションの順番 | Yes |
lambda_action
設定 | 内容 | 必須の有無 |
---|---|---|
function_arn |
InvokeさせるLambda関数のARN | Yes |
invocation_type |
Lambda関数の実行方法( Event または RequestResponse ) |
No |
topic_arn |
アクション実行時に通知するSNSトピックARN | No |
postion |
ルール内でのアクションの順番 | Yes |
s3_action
設定 | 内容 | 必須の有無 |
---|---|---|
bucket_name |
受信したメールを保存するS3バケット名 | Yes |
kms_key_arn |
S3に保存する際にKMSで暗号化するか | No |
object_key_prefix |
S3に保存する際に利用するプレフィックス | No |
topic_arn |
アクション実行時に通知するSNSトピックARN | No |
postion |
ルール内でのアクションの順番 | Yes |
sns_action
設定 | 内容 | 必須の有無 |
---|---|---|
topic_arn |
通知するSNSトピックARN | Yes |
postion |
ルール内でのアクションの順番 | Yes |
stop_action
設定 | 内容 | 必須の有無 |
---|---|---|
scope |
ストップアクションのスコープ | Yes |
topic_arn |
アクション実行時に通知するSNSトピックARN | No |
postion |
ルール内でのアクションの順番 | Yes |
workmail_action
設定 | 内容 | 必須の有無 |
---|---|---|
organization_arn |
アクションと関連付けるWorkMailのARN | Yes |
topic_arn |
アクション実行時に通知するSNSトピックARN | No |
postion |
ルール内でのアクションの順番 | Yes |
今回は以前メール受信設定をAWS CLIでセットアップしたこちらのエントリと同じように以下の設定をしてみます。ただしコードが長くなるので全てのアクションを1つのルールに含めています。
- テスト用ヘッダを追加
- S3アクションで受信したメールをバケットに保存
- SMTP Replyコード:550かつSMTPステータスコード:5.5.1の場合にバウンスアクションを実行
- Lambda関数をInvoke
- SNSトピックへ通知
-
rule-set/ses.tf
# ルールセットの作成 resource "aws_ses_receipt_rule_set" "ses" { rule_set_name = "tf-rule-set" } # ルールの作成 resource "aws_ses_receipt_rule" "ses" { name = "tf-rule" rule_set_name = "${aws_ses_receipt_rule_set.ses.rule_set_name}" recipients = ["${var.ses_config["verification_domain"]}"] enabled = true scan_enabled = true # ヘッダ追加アクション add_header_action { header_name = "TestHeader" header_value = "Test" position = 1 } # バウンスアクション bounce_action { message = "Mailbox does not exist" sender = "bounce@${var.ses_config["verification_domain"]}" smtp_reply_code = 550 topic_arn = "${module.tf_sns_email.arn}" status_code = "5.1.1" position = 2 } # Lambdaアクション lambda_action { function_arn = "${aws_lambda_function.lambda.arn}" invocation_type = "${var.ses_config["invocation_type"]}" position = 3 } # S3アクション s3_action { bucket_name = "${aws_s3_bucket.s3.id}" object_key_prefix = "${var.ses_config["object_key_prefix"]}" position = 4 } # SNSアクション sns_action { topic_arn = "${module.tf_sns_email.arn}" position = 5 } } # 作成したルールセットを有効化 resource "aws_ses_active_receipt_rule_set" "ses" { rule_set_name = "${aws_ses_receipt_rule_set.ses.rule_set_name}" }
Terraform実行後以下のコマンドでルールセットの内容を確認できます。
$ aws ses describe-active-receipt-rule-set { "Rules": [ { "Name": "tf-rule", "Recipients": [ "<_YOUR_VERIFICATION_DOMAIN_>" ], "Enabled": true, "ScanEnabled": true, "Actions": [ { "AddHeaderAction": { "HeaderName": "TestHeader", "HeaderValue": "Test" } }, { "BounceAction": { "Message": "Mailbox does not exist", "Sender": "bounce@<_YOUR_VERIFICATION_DOMAIN_>", "SmtpReplyCode": "550", "TopicArn": "arn:aws:sns:us-east-1:************:<_YOUR_SNS_TOPIC_>", "StatusCode": "5.1.1" } }, { "LambdaAction": { "InvocationType": "Event", "FunctionArn": "arn:aws:lambda:us-east-1:************:function:<_YOUR_LAMBDA_FUNCTION_>" } }, { "S3Action": { "KmsKeyArn": "", "ObjectKeyPrefix": "ses", "BucketName": "<_YOUR_S3_BUCKET_>" } }, { "SNSAction": { "TopicArn": "arn:aws:sns:us-east-1:************:<_YOUR_SNS_TOPIC_>", "Encoding": "UTF-8" } } ], "TlsPolicy": "Optional" } ], "Metadata": { "CreatedTimestamp": "2016-12-28T03:13:53.180Z", "Name": "tf-rule-set" } }
設定が完了したら動作を確認してみます。まず正常にメールが受信できた場合の動作を確認してみます。SNSトピックへのパブリッシュは今回メールを利用したのでメールが通知されたことをもって確認しました。
# ルール適用対象のドメインに対してテスト用メールの送信 $ aws ses send-email \ --from aaa@$domain \ --to bbb@$domain \ --subject subject \ --text body { "MessageId": "0100015943cc6d48-28423e45-35c6-401f-a1d6-4dba77fb3308-000000" } # S3バケットにメールがputされていることを確認 $ aws s3 ls s3://<_YOUR_S3_BUCKET_> --recursive 2016-12-28 13:59:12 645 ses/AMAZON_SES_SETUP_NOTIFICATION 2016-12-28 14:03:18 3475 ses/j363mv5dj9c1bg22fpbgfi4ree9feepvmis2k501 # Lambda関数がInvokeされていることを確認 $ aws logs get-log-events \ --log-group-name <_YOUR_LOG_GROUP_> \ --log-stream-name <_YOUR_LOG_STREAM_> { "nextForwardToken": "f/33069806205607788645082847192957270138853351933253713922", "events": [ { "ingestionTime": 1482901205029, "timestamp": 1482901189833, "message": "START RequestId: 75eba679-ccba-11e6-a8e5-7fab800e61d8 Version: $LATEST\n" }, <snip> # 指定したヘッダが追加されていることを確認 $ aws s3 cp s3://<_YOUR_S3_BUCKET_>/ses/j363mv5dj9c1bg22fpbgfi4ree9feepvmis2k501 - \ | grep -F 'TestHeader' TestHeader: Test
続いてバウンスメール発生時の動作を確認してみます。
# バウンスメールの送信 $ aws ses send-email \ --from aaa@$domain \ --to [email protected] \ --subject subject \ --text body { "MessageId": "0100015943d6bfad-cfb7cef1-1ab4-4214-bbef-5136730c6cc5-000000" }
バウンスメール送信後、以下のような内容が記述されたバウンスメールがSNS経由で届いていれば成功です。
"action": { "type": "Bounce", "topicArn": "arn:aws:sns:us-east-1:************:<_YOUR_SNS_TOPIC_>", "smtpReplyCode": "550", "statusCode": "5.1.1", "message": "Mailbox does not exist", "sender": "bounce@<_YOUR_VERIFICATION_DOMAIN_>" }
まとめ
いかがだったでしょうか。
Terraformを利用したSESのIPアドレスフィルタ及びアクションの設定方法についてご紹介しました。次回はメール送信関連のリソースについてご紹介したいと思います。
本エントリがみなさんの参考になれば幸いに思います。